from svg.path import parse_path
from xml.dom import minidom
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon as pltPolygon
import itertools
from shapely.geometry import LineString, MultiPolygon, Polygon, GeometryCollection, Point, MultiLineString, LinearRing
from shapely.ops import split, linemerge, unary_union
from shapely.affinity import scale
from decimal import Decimal
import re
from xml.dom import minidom
from shapely.geometry import Polygon, LineString

ndigits = 2

def get_longest_decimal_places(path_string):
    # 使用正则表达式提取所有数值
    numbers = re.findall(r"[-+]?\d*\.\d+|\d+", path_string)

    # 遍历数值并找到最长的小数位
    longest_decimal_places = 0
    for number in numbers:
        decimal_places = len(number.split(".")[-1]) if "." in number else 0
        longest_decimal_places = max(longest_decimal_places, decimal_places)

    return longest_decimal_places



# Define a function to parse the SVG file and extract line elements as shapely LineStrings
def parse_svg_and_create_polygon(svg_content):
    # Parse the SVG content
    doc = minidom.parseString(svg_content)
    line_elements = doc.getElementsByTagName('line')
    lines = []

    # Extract the lines and their coordinates
    for line in line_elements:
        x1, y1, x2, y2 = [float(line.getAttribute(coord)) for coord in ['x1', 'y1', 'x2', 'y2']]
        lines.append(((x1, y1), (x2, y2)))

    doc.unlink()

    # Sort lines to make sure they form a continuous path
    # Start from the first line and find the next line which starts where the last one ended
    sorted_lines = [lines.pop(0)]  # Start with the first line

    while lines:
        last_line = sorted_lines[-1][1]
        for i, line in enumerate(lines):
            if last_line == line[0]:  # If the line starts where the last one ended
                sorted_lines.append(lines.pop(i))
                break
            elif last_line == line[1]:  # If the line ends where the last one ended, we need to reverse it
                sorted_lines.append((line[1], line[0]))
                lines.pop(i)
                break
        else:
            # If we didn't find any line to continue, it means we have a disjoint set of lines
            # Let's start with a new line
            sorted_lines.append(lines.pop(0))

    # Create a LinearRing from the sorted lines
    linear_ring = LinearRing([point for line in sorted_lines for point in line])

    # Create a Polygon from the LinearRing
    polygon = Polygon(linear_ring)

    return polygon

# 获取长方形的宽与长，如果输入的不是polygon，则报错
# TODO 检查输入的Polygon是否是长方形
def get_width_length(polygon: Polygon) -> (float, float):
    if not isinstance(polygon, Polygon):
        print(polygon)
        raise ValueError("Input must be a Shapely Polygon")

    minx, miny, maxx, maxy = polygon.bounds
    width = maxx - minx
    length = maxy - miny

    return tuple(sorted((width, length)))

# 检查一个长方形是否能被最大模块面积包络
def is_feasible(rectangle, max_width, max_length):
    width, length = get_width_length(rectangle)
    return (width <= max_width and length <= max_length) or (width <= max_length and length <= max_width)

# 绘画一个Multipolygon，c_f用于控制是否指示超限的长方形
def draw_multipolygon(multipolygon, c_f=False):
    plt.axis('equal')
    for each in list(multipolygon.geoms):
        x, y = each.exterior.xy
        if c_f==False:
            plt.plot(x, y, 'o', color='#f16824', zorder=1)
            plt.fill(x, y, color='#6699cc', alpha=0.5, zorder=2)

        if c_f==True:
            plt.plot(x, y, 'o', color='#f16824', zorder=1)
            if is_feasible(each, max_width, max_length):
                plt.fill(x, y, color='#6699cc', alpha=0.5, zorder=2)
            else:
                plt.fill(x, y, color='r', alpha=0.5, zorder=2)

# 检查一个Layout是否每一个分块都可以被最大模块面积包络
def layout_is_feasible(multipolygon, max_width, max_length):
    flag = True
    for each in list(multipolygon.geoms):
        if is_feasible(each, max_width, max_length):
            pass
        else:
            flag = False
            return flag
    return flag

# 检查一条线是否被一个多边形包含（闭集，即和边重合也算）
def is_close_contained(line, polygon):
    gc = line.intersection(polygon)
    if isinstance(gc, GeometryCollection):
        for each in list(gc.geoms):
            if polygon.contains(each) or polygon.touches(each):
                pass
            else:
                return False
        return True
    elif isinstance(gc, LineString):
        if polygon.contains(gc) or polygon.touches(gc):
            return True
        else:
            return False
    else:
        raise ImportError

def clsoed_extended_line(line, polygon, s_factor = 10):
    coords = np.array(line.coords)
    p1 = coords[0]
    p2 = coords[1]
    ep1 = np.around(((p1+p2)/2 - (p2-p1) * s_factor), ndigits)
    ep2 = np.around(((p1+p2)/2 + (p2-p1) * s_factor), ndigits)
    eline = LineString([ep1, ep2])
    # 计算延长线与polygon的交点
    intersections = eline.intersection(polygon)
     # 如果intersection包括多条线，线和polygon有交集（被分段），反之则没有
    if isinstance(intersections, MultiLineString):
        #使用linemerge将多段连续的线合并，如果不连续返回MultiString
        merged_lines = linemerge(intersections)
        #对于MultiString仅保留包含原来edge的那一部分
        if isinstance(merged_lines, MultiLineString):
            
            for each in merged_lines.geoms:
                if each.covers(line):
                    return each
        #如果仅返回LineString，说明整体连续
        else:
            return merged_lines
    else:
        return intersections


def extend_polygon_edges(polygon):
    # 获取polygon的坐标点
    coords = list(polygon.exterior.coords)
    extended_lines = []

    # 遍历polygon的边
    for i in range(len(coords) - 1):
        # 获取当前边的起点和终点
        p1, p2 = np.array(coords[i]), np.array(coords[i + 1])
        edge = LineString([p1, p2])
        extended_lines.append(clsoed_extended_line(edge, polygon, 50))
    # 返回延长线列表
    return extended_lines

# 对一个超限的长方形进行划分
def seg_rectangle(rectangle, max_width):
    width, length = get_width_length(rectangle)
    minx, miny, maxx, maxy = rectangle.bounds
    seg_lines = []

    d_w = int(np.ceil(width / max_width))
    d_l = int(np.ceil(length / max_width))
    pace_w = width / d_w
    pace_l = length / d_l

    for i in range(1, d_w):
        p1 = (minx + i * pace_w, miny)
        p2 = (minx + i * pace_w, maxy)
        segline = LineString([p1, p2])
        seg_lines.append(segline)

    for i in range(1, d_l):
        p1 = (minx, miny + i * pace_l)
        p2 = (maxx, miny + i * pace_l)
        segline = LineString([p1, p2])
        seg_lines.append(segline)

    return seg_lines

def seg_layout(multipolygon, max_width, max_length, polygon):
    # 检查是否还有超限矩形
    while any(not is_feasible(geom, max_width, max_length) for geom in multipolygon.geoms):
        # 找到最大面积的超限矩形
        max_area_geom = max((geom for geom in multipolygon.geoms if not is_feasible(geom, max_width, max_length)), key=lambda x: x.area)

        # 获取该矩形的分割线
        seg_lines = seg_rectangle(max_area_geom, max_width)
        extended_lines = [clsoed_extended_line(line, polygon) for line in seg_lines]

        # 使用分割线对整个多边形进行分割
        for line in extended_lines:
            try:
                multipolygon = split(multipolygon, line)
            except:
                pass

        # 将分割后的结果转换为MultiPolygon
        multipolygon = MultiPolygon(list(multipolygon.geoms))

    return multipolygon


